﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CSharpExperiments.Metaprogramming;

namespace CSharpExperiments
{
	/// <summary>A Vector with typed length using Natural number type. Indicies are 1-based.</summary>
	/// <remarks>These can just be renamed to "Vector" if you're only going to use this implementation of vector.</remarks>
	public static class NVector
	{
		public static NVector<T, Zero> Create<T>() { return new NVectorImpl<T, Zero>(); }
		public static NVector<T, Nat1> Create<T>(T v1) { return new NVectorImpl<T, Nat1>(v1); }
		public static NVector<T, Nat2> Create<T>(T v1, T v2) { return new NVectorImpl<T, Nat2>(v1, v2); }
		public static NVector<T, Nat3> Create<T>(T v1, T v2, T v3) { return new NVectorImpl<T, Nat3>(v1, v2, v3); }
		public static NVector<T, Nat4> Create<T>(T v1, T v2, T v3, T v4) { return new NVectorImpl<T, Nat4>(v1, v2, v3, v4); }
		public static NVector<T, Nat5> Create<T>(T v1, T v2, T v3, T v4, T v5) { return new NVectorImpl<T, Nat5>(v1, v2, v3, v4, v5); }
		public static NVector<T, Nat6> Create<T>(T v1, T v2, T v3, T v4, T v5, T v6) { return new NVectorImpl<T, Nat6>(v1, v2, v3, v4, v5, v6); }
		public static NVector<T, Nat7> Create<T>(T v1, T v2, T v3, T v4, T v5, T v6, T v7) { return new NVectorImpl<T, Nat7>(v1, v2, v3, v4, v5, v6, v7); }
		public static NVector<T, Nat8> Create<T>(T v1, T v2, T v3, T v4, T v5, T v6, T v7, T v8) { return new NVectorImpl<T, Nat8>(v1, v2, v3, v4, v5, v6, v7, v8); }

		public static T Get<T, N, I>(this NVector<T, N> vec, I index) where I : Nat1, new() where N : I, new()
		{
			return vec.Do<T>("GetUnsafe", Naturals.Value<I>());
		}

		public static IEnumerable<T> Values<T, N>(this NVector<T, N> vec) where N : Zero, new()
		{
			var get = vec.GetFunc<int, T>("GetUnsafe");

			for (int i = 1; i <= Naturals.Value<N>(); ++i)
				yield return get(i);
		}

		private class NVectorImpl<T, N> : NVector<T, N> where N : Zero, new()
		{
			private T[] vals;

			internal NVectorImpl(params T[] vals) { this.vals = vals; }

			[Obsolete("Don't use directly, use the Get(Natural) method", true)]
			public T GetUnsafe(int indexFrom1) { return vals[indexFrom1 - 1]; }

			public int Length { get { return Naturals.Value<N>(); } }
		}
	}

	public interface NVector<T, out N> where N : Zero
	{
		int Length { get; }
	}
}
